package mymyoux.io.apps
{
	import flash.utils.ByteArray;
	import flash.utils.clearTimeout;
	import flash.utils.setTimeout;
	
	import mymyoux.debug.Log;
	import mymyoux.io.SimpleSocket;
	import mymyoux.io.apps.ftpClasses.FTPCodes;
	import mymyoux.io.apps.ftpClasses.FTPCommands;
	import mymyoux.utils.TamponFunctions;
	
	import org.osflash.signals.Signal;
	public class FTP
	{
		protected var _socket:SimpleSocket;
		public var informations:Signal;
		public var errors:Signal;
		protected var _login:String;
		protected var _password:String;
		protected var _allowedCommands:Array;
		protected var _passiveSocket:SimpleSocket;
		protected var _passiveMode:Boolean;
		protected var _getAllowedCommandsAtStartup:Boolean;
		protected var _noopDelay:uint;
		protected var _noopDelayTimer:int;
		protected var _commands:Array;
		protected var _params:Array;
		protected var _binary:Boolean;
		protected var _waiting:Boolean;
		protected var _tampon:TamponFunctions;
		public function FTP(ip:String, port:uint = 21, login:String="anonymous", password:String="anonymous@anonymous.org" )
		{
			informations = new Signal(uint,String);
			errors = new Signal(uint,String);
			
			_waiting = false;
			_tampon = new TamponFunctions();
			_commands = new Array();
			_params = new Array();
			_passiveMode = true;
			_getAllowedCommandsAtStartup = true;
			_noopDelay = 30000;
			_noopDelayTimer = -1;
			_binary = false;
			
			_allowedCommands = new Array();
			this.login = login;
			_password = password;
			_socket = new SimpleSocket();
			_socket.blockedDataSignal.add(blockedData);
			_socket.connexionSignal.add(connexion);
			_socket.dataSignal.add(onData);
			_socket.createSocket(ip,port);
		}
		protected function get login():String
		{
			return _login;
		}
		protected function set login(value:String):void
		{
			if(value == null || value.length == 0)
			{
				_login = "anonymous";
			}else
			{
				_login = value;
			}
		}
		public function help():void
		{
			execute(FTPCommands.HELP);
		}
		protected function connexion(connected:Boolean):void
		{
			if(connected)
			{
				execute(FTPCommands.USER, _login);
			}
		}
		protected function blockedData(data:Object):void
		{
			Log.error("blocked data:"+data);	
		}
		public function execute(command:String, param:String = null):void
		{
			if(isAllowed(command))
			{
				if(!_waiting)
				{
					_waiting = true;
					_commands.push(command);
					_params.push(param);
					while(_commands.length > 100)
					{
						_commands.shift();
						_params.shift();
					}
					if (param == null || param.length == 0)
					{
						_socket.writeString(command);
					}else
					{
						_socket.writeString(command+" "+param);
					}
					if (_noopDelayTimer != -1)
					{
						clearTimeout(_noopDelayTimer);
						_noopDelayTimer = -1;
					}
					if(_noopDelay > 0)
					{
						_noopDelayTimer = setTimeout(noop, _noopDelay);
					}
				}else
				{
					_tampon.add(execute, this, command, param);
				}
			}else
			{
				errors.dispatch(FTPCodes.UNIMPLEMENTED,command);
			}
		}
		public function shutdown():void
		{
			if(isAllowed(FTPCommands.QUIT))
			{
				execute(FTPCommands.QUIT);
			}else
			{
				execute(FTPCommands.BYE);
			}
		}
		protected function onData(data:ByteArray):void
		{
			if (_noopDelayTimer != -1)
			{
				clearTimeout(_noopDelayTimer);
				_noopDelayTimer = -1;
			}
			
			var lines:Array = data.toString().split("\r\n");
			var index:uint;
			var line:String;
			var i:int;
			var len:int = lines.length;
			for (i=0; i<len; i++)
			{
				line = lines[i];
				if (line.length > 0)
				{
					try
					{
						index = uint(Number(line.substring(0,3)));
						line = line.substring(4);
					}catch(error:Error)
					{
						index = 0;
					}
					switch(index)
					{
						case FTPCodes.HELP:
						Log.debug(lines);
							informations.dispatch(index,line);	
							_allowedCommands = new Array();
							index = 0;
							while (index == 0)
							{
								try
								{
									line = lines[++i];
									index = uint(Number(line.substring(0,3)));
									
									if(index == 0)
									{
										_allowedCommands.push(line);
										
									}else
									{
										line = line.substring(4);
										if (line.substring(line.length-1) != "*")
										{
											informations.dispatch(index,_allowedCommands.toString());	
										}
										informations.dispatch(index,line);	
									}
								}catch(error:Error)
								{
									_allowedCommands.push(line);
									index = 0;
								}	
							}			
						break;
						case FTPCodes.LOGIN_SUCCESSFULL:
							informations.dispatch(index,line);
							if(getAllowedCommandsAtStartup)
							{
								help();
							}
						break;
						case FTPCodes.MESSAGE:
							informations.dispatch(index,line);
						break;
						case FTPCodes.PASSWORD_REQUIRED:
							execute(FTPCommands.PASS, _password );
						break;
						case FTPCodes.LOGIN_UNSUCCESSFULL:
							errors.dispatch(index,line);
						break;
						case FTPCodes.SYNTAX_ERROR:
							errors.dispatch(index,line);
						break;
						case FTPCodes.CLOSE:
							informations.dispatch(index,line);
						break;		
						case FTPCodes.MKD_ALREADY_EXISTS:
						case FTPCodes.MKD_PERMISSION_DENIED:
							informations.dispatch(index,line);
						break;
						case FTPCodes.MKD_SYNTAX_ERROR:
							errors.dispatch(index,line);
						break;
						case FTPCodes.MKD_SUCCESSFULL:
							informations.dispatch(index,line);
						break;
						case FTPCodes.OK:
						case FTPCodes.ASCII:
						case FTPCodes.BINARY:
						case FTPCodes.NOOP_OK:
							if(line.toLowerCase().indexOf("ascii") > -1)
							{
								_binary = false;
							}else
							{
								if(line.toLowerCase().indexOf("binary") > -1)
								{
									_binary = true;
								}else
								{
									if(line.toLowerCase().indexOf("ok") > -1)
									{
										//NOOP
									}
								}
							}
							informations.dispatch(index,line);
						break;
						case FTPCodes.PASSIVE_MODE:
							_passiveMode = true;
							index = line.indexOf("(");
							Log.debug(line+":"+index);
							if(index > -1)
							{
								var chaine:String = line.substring(index + 1,line.lastIndexOf(")"));
								var datas:Array = chaine.split(",");
								var port:int = Number(datas.pop().toString()) + 256 * Number(datas.pop().toString());
								var ip:String = datas.join(".");
								if( _passiveSocket != null)
								{
									_passiveSocket.dataSignal.removeAll();
									_passiveSocket.connexionSignal.removeAll();
								}
								_passiveSocket = new SimpleSocket();
								_passiveSocket.dataSignal.add(passiveData);
								_passiveSocket.connexionSignal.add(passiveConnexion);
								_passiveSocket.createSocket(ip, port);
								execute(_commands[_commands.length - 2], _params[_params.length - 2]);
							}else
							{
								errors.dispatch(index,"Incorrect");
							}
						
						break;
						case FTPCodes.CANT_OPEN_CONNECTION:
							//TRY PASSIVEMODE
							setPassiveMode();
						break;
						
						default:
							errors.dispatch(index,line);
					}
					_waiting = false;
					if (_tampon.hasNext())
					{
						_tampon.next();
					}
					
				}
			}
			if(_noopDelay > 0)
			{
				_noopDelayTimer = setTimeout(noop, _noopDelay);
			}
		}
		public function get passiveMode():Boolean
		{
			return _passiveMode;
		}
		public function set passiveMode(value:Boolean):void
		{
			if(value != _passiveMode && value)
			{
				setPassiveMode();
			}
			_passiveMode = value;
		}
		protected function setPassiveMode():void
		{
			execute(FTPCommands.PASV);
		}
		protected function passiveConnexion(success:Boolean):void
		{
			Log.debug("passive connected:"+success);
		}
		protected function passiveData(b:ByteArray):void
		{
			if(_binary)
			{
				Log.debug("BINARY:"+b.length);
			}else
			{
				var result:String = b.toString();
				Log.debug("ASCII:"+result);
			}
		}
		public function noop():void
		{
			execute(FTPCommands.NOOP);
		}
		public function get noopDelay():uint
		{
			return _noopDelay;
		}
		public function set noopDelay(value:uint):void
		{
			_noopDelay = value;
			if (_noopDelayTimer == -1 && _noopDelay > 0)
			{
				_noopDelayTimer = setTimeout(noop, _noopDelay);
			}
			if (_noopDelayTimer != -1 && _noopDelay == 0)
			{
				clearTimeout(_noopDelayTimer);
				_noopDelayTimer = -1;
			}
		}
		public function get getAllowedCommandsAtStartup():Boolean
		{
			return _getAllowedCommandsAtStartup;
		}
		public function set getAllowedCommandsAtStartup(value:Boolean):void
		{
			_getAllowedCommandsAtStartup = getAllowedCommandsAtStartup;
		}
		public function createFolder(path:String):void
		{
			if (path.length > 0)
			{
				if(isAllowed(FTPCommands.MKD))
				{
					execute(FTPCommands.MKD, path);
				}else
				{
					execute(FTPCommands.XMKD, path);
				}	
			}else
			{
				errors.dispatch(FTPCodes.MKD_SYNTAX_ERROR,"Folder's name incorrect");
			}
		}
		public function isAllowed(command:String):Boolean
		{
			return _allowedCommands.length < 10 || _allowedCommands.indexOf(command) > -1;
		}
		public function getList():void
		{
			execute(FTPCommands.LIST);
		}
		public function setType():void{}
	}
}